/**HEADER********************************************************************
* 
* Copyright (c) 2008-2010 Freescale Semiconductor;
* All Rights Reserved
*
* Copyright (c) 2004-2010 Embedded Access Inc.;
* All Rights Reserved
*
* Copyright (c) 1989-2008 ARC International;
* All Rights Reserved
*
*************************************************************************** 
*
* THIS SOFTWARE IS PROVIDED BY FREESCALE "AS IS" AND ANY EXPRESSED OR 
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  
* IN NO EVENT SHALL FREESCALE OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 
* THE POSSIBILITY OF SUCH DAMAGE.
*
**************************************************************************
*
* $FileName: i2c_pol_mpc83xx.c$
* $Version : 3.8.4.2$
* $Date    : Feb-13-2012$
*
* Comments:
*
*    This file contains the lowlevel polled mode functions for the 
*    I2C module of mpc83xx.
*
*END*********************************************************************/

#include <mqx.h>
#include <bsp.h>
#include <io_prv.h>
#include <fio_prv.h>
#include "i2c.h"
#include "i2c_pol_prv.h"
#include "i2c_mpc83xx_prv.h"


extern _mqx_uint _mpc83xx_i2c_polled_init(MPC83xx_I2C_INIT_STRUCT_PTR, pointer _PTR_, char_ptr);
extern _mqx_uint _mpc83xx_i2c_polled_deinit(IO_I2C_POLLED_DEVICE_STRUCT_PTR, VMPC83xx_I2C_INFO_STRUCT_PTR);
extern _mqx_uint _mpc83xx_i2c_polled_rx(IO_I2C_POLLED_DEVICE_STRUCT_PTR, char_ptr, int_32);
extern _mqx_uint _mpc83xx_i2c_polled_tx(IO_I2C_POLLED_DEVICE_STRUCT_PTR, char_ptr, int_32);
extern _mqx_uint _mpc83xx_i2c_polled_ioctl(VMPC83xx_I2C_INFO_STRUCT_PTR, uint_32, uint_32_ptr);
static uint_8  _mpc83xx_i2c_find_baud_index (uint_32);
static _mqx_uint _mpc83xx_i2c_polled_rx_tx (VMPC83xx_I2C_STRUCT_PTR, VMPC83xx_I2C_INFO_STRUCT_PTR, char_ptr, uint_32);



/* I2C frequency divider values taken from the MPC83xxERM.pdf, Table 21-5. */
uint_16 ic_div[] = {  384,   416,   480,   576,   640,   704,   832,  1024,
                     1152,  1280,  1536,  1920,  2304,  2560,  3072,  3840,
                     4608,  5120,  6144,  7680,  9216, 10240, 12288, 15360,
                    18432, 20480, 24576, 30720, 36864, 40960, 49152, 61440,
                      256,   288,   320,   352,   384,   448,   512,   576,
                      640,   768,   896,  1024,  1280,  1536,  1792,  2048,
                     2560,  3072,  3584,  4096,  5120,  6144,  7168,  8192,
                    10240, 12288, 14336, 16384, 20480, 24576, 28672, 32768
                   };
/* I2C frequency divider values taken from the MCF5282UM.pdf, Figure 24-3. */
/* duplicated items are commented out, if commented out value is wanted, uncoment it,
   comment out duplicated and rebuilt bsp */
static const MPC83xx_I2C_BAUDRATE_STRUCT _i2c_baudrate_tbl[] = {
  {BSP_I2C_CLOCK/61440,      0x1F}, 
  {BSP_I2C_CLOCK/49152,      0x1E}, 
  {BSP_I2C_CLOCK/40960,      0x1D}, 
  {BSP_I2C_CLOCK/36864,      0x1C}, 
  {BSP_I2C_CLOCK/32768,      0x3F},
  {BSP_I2C_CLOCK/30720,      0x1B}, 
  {BSP_I2C_CLOCK/28672,      0x3E}, 
  {BSP_I2C_CLOCK/24576,      0x3D}, 
  //{BSP_I2C_CLOCK/24576,      0x1A}, 
  {BSP_I2C_CLOCK/20480,      0x3C}, 
  //{BSP_I2C_CLOCK/20480,      0x19}, 
  {BSP_I2C_CLOCK/18432,      0x18}, 
  {BSP_I2C_CLOCK/16384,      0x3B}, 
  {BSP_I2C_CLOCK/15360,      0x17}, 
  {BSP_I2C_CLOCK/14336,      0x3A}, 
  {BSP_I2C_CLOCK/12288,      0x39}, 
  //{BSP_I2C_CLOCK/12288,      0x16}, 
  {BSP_I2C_CLOCK/10240,      0x38}, 
  //{BSP_I2C_CLOCK/10240,      0x15}, 
  {BSP_I2C_CLOCK/9216,       0x14}, 
  {BSP_I2C_CLOCK/8192,       0x37}, 
  {BSP_I2C_CLOCK/7680,       0x13}, 
  {BSP_I2C_CLOCK/7168,       0x36}, 
  {BSP_I2C_CLOCK/6144,       0x35}, 
  //{BSP_I2C_CLOCK/6144,       0x12}, 
  {BSP_I2C_CLOCK/5120,       0x34}, 
  //{BSP_I2C_CLOCK/5120,       0x11}, 
  {BSP_I2C_CLOCK/4608,       0x10}, 
  {BSP_I2C_CLOCK/4096,       0x33}, 
  {BSP_I2C_CLOCK/3840,       0x0F}, 
  {BSP_I2C_CLOCK/3072,       0x31}, 
  {BSP_I2C_CLOCK/3072,       0x0E}, 
  {BSP_I2C_CLOCK/3584,       0x32}, 
  {BSP_I2C_CLOCK/2560,       0x30}, 
  //{BSP_I2C_CLOCK/2560,       0x0D}, 
  {BSP_I2C_CLOCK/2304,       0x0C}, 
  {BSP_I2C_CLOCK/2048,       0x2F}, 
  {BSP_I2C_CLOCK/1920,       0x0B}, 
  {BSP_I2C_CLOCK/1792,       0x2E}, 
  {BSP_I2C_CLOCK/1536,       0x0A}, 
  //{BSP_I2C_CLOCK/1536,       0x2D}, 
  {BSP_I2C_CLOCK/1280,       0x2C}, 
  //{BSP_I2C_CLOCK/1280,       0x09}, 
  {BSP_I2C_CLOCK/1152,       0x08}, 
  {BSP_I2C_CLOCK/1024,       0x2B}, 
  //{BSP_I2C_CLOCK/1024,       0x07}, 
  {BSP_I2C_CLOCK/896,        0x2A}, 
  {BSP_I2C_CLOCK/832,        0x06}, 
  {BSP_I2C_CLOCK/768,        0x29}, 
  {BSP_I2C_CLOCK/704,        0x05},
  {BSP_I2C_CLOCK/640,        0x28}, 
  //{BSP_I2C_CLOCK/640,        0x04}, 
  {BSP_I2C_CLOCK/576,        0x27}, 
  //{BSP_I2C_CLOCK/576,        0x03}, 
  {BSP_I2C_CLOCK/512,        0x26}, 
  {BSP_I2C_CLOCK/480,        0x02}, 
  {BSP_I2C_CLOCK/448,        0x25}, 
  {BSP_I2C_CLOCK/416,        0x01}, 
  {BSP_I2C_CLOCK/384,        0x24}, 
  //{BSP_I2C_CLOCK/384,        0x00}, 
  {BSP_I2C_CLOCK/352,        0x23}, 
  {BSP_I2C_CLOCK/320,        0x22}, 
  {BSP_I2C_CLOCK/288,        0x21}, 
  {BSP_I2C_CLOCK/256,        0x20}, 
};


/*FUNCTION****************************************************************
* 
* Function Name    : _mpc83xx_i2c_find_baud_index
* Returned Value   : index into table of baudrate dividers
* Comments         :
*    Find best I2C setting for given baudrate.
*
*END*********************************************************************/

static uint_8 _mpc83xx_i2c_find_baud_index 
   (
      /* [IN] Desired baudrate */
      uint_32 input_baud
   )
{
   uint_8 low, high, i;
   
   low = 0; 
   high = sizeof(_i2c_baudrate_tbl)/sizeof(MPC83xx_I2C_BAUDRATE_STRUCT) - 1;
   do 
   {
      i = (low + high) >> 1;
      if (input_baud < _i2c_baudrate_tbl[i].BAUD_RATE)
      {
         high = i - 1;
      } else {
         low = i + 1;
      }
   } while ((_i2c_baudrate_tbl[i].BAUD_RATE != input_baud) && (high >= low));
   
   /* in case we did not found exact BAUD rate use nearest slower settings than requested */
   if ((_i2c_baudrate_tbl[i].BAUD_RATE > input_baud) && (i != 0))
   {
      i--;
   }
   
   return i;
}


/*FUNCTION****************************************************************
* 
* Function Name    : _mpc83xx_i2c_polled_install
* Returned Value   : MQX error code
* Comments         :
*    Install an I2C device.
*
*END*********************************************************************/

_mqx_uint _mpc83xx_i2c_polled_install
   (
      /* [IN] A string that identifies the device for fopen */
      char_ptr identifier,
  
      /* [IN] The I/O init data pointer */
      pointer  init_data_ptr
   )
{ /* Body */

   return _io_i2c_polled_install(identifier,
      (uint_32 (_CODE_PTR_)(pointer, pointer _PTR_, char_ptr))_mpc83xx_i2c_polled_init,
      (uint_32 (_CODE_PTR_)(pointer, pointer))_mpc83xx_i2c_polled_deinit,
      (int_32 (_CODE_PTR_)(pointer, char_ptr, int_32))_mpc83xx_i2c_polled_rx,
      (int_32 (_CODE_PTR_)(pointer, char_ptr, int_32))_mpc83xx_i2c_polled_tx,
      (int_32 (_CODE_PTR_)(pointer, uint_32, uint_32_ptr))_mpc83xx_i2c_polled_ioctl, 
      (pointer)init_data_ptr);

} /* Endbody */


/*FUNCTION****************************************************************
* 
* Function Name    : _mpc83xx_i2c_polled_init
* Returned Value   : MQX_OK or a mqx error code
* Comments         :
*    This function initializes an I2C device.
*
*END*********************************************************************/

_mqx_uint _mpc83xx_i2c_polled_init
   (
      /* [IN] Initialization information for the device being opened */
      MPC83xx_I2C_INIT_STRUCT_PTR io_init_ptr,

      /* [OUT] Address to store device specific information */
      pointer _PTR_               io_info_ptr_ptr,
       
      /* [IN] The rest of the name of the device opened */
      char_ptr                    open_name_ptr

   )
{ /* Body */
   VMPC83xx_I2C_STRUCT_PTR        i2c_ptr;
   VMPC83xx_I2C_INFO_STRUCT_PTR   io_info_ptr;

   i2c_ptr = _bsp_get_i2c_base_address (io_init_ptr->CHANNEL);
   if (NULL == i2c_ptr)
   {
        return I2C_ERROR_CHANNEL_INVALID;
   }
  
   io_info_ptr = (VMPC83xx_I2C_INFO_STRUCT_PTR) _mem_alloc_system_zero((uint_32)sizeof(MPC83xx_I2C_INFO_STRUCT));
   if (NULL == io_info_ptr) 
   {
      return MQX_OUT_OF_MEMORY;
   }
   _mem_set_type ((pointer)io_info_ptr, MEM_TYPE_IO_I2C_INFO_STRUCT);

   /* I2C board specific initialization */
   _bsp_i2c_io_init (io_init_ptr->CHANNEL);

   *io_info_ptr_ptr                              = (pointer)io_info_ptr;
   io_info_ptr->INIT                             = *io_init_ptr;
   io_info_ptr->I2C_PTR                          = i2c_ptr;
   io_info_ptr->OLD_ISR                          = NULL;
   io_info_ptr->OLD_ISR_DATA                     = NULL;
   io_info_ptr->VECTOR                           = 0;
   io_info_ptr->MODE                             = io_init_ptr->MODE;
   io_info_ptr->STATE                            = I2C_STATE_READY;
   io_info_ptr->ADDRESSEE                        = 0;
   io_info_ptr->OPERATION                        = 0;
   io_info_ptr->INT_REQUEST                      = 0;
   io_info_ptr->RX_REQUEST                       = 0;
   io_info_ptr->RX_BUFFER                        = NULL;
   io_info_ptr->RX_IN                            = 0;
   io_info_ptr->RX_OUT                           = 0;
   io_info_ptr->TX_BUFFER                        = NULL;
   io_info_ptr->TX_IN                            = 0;
   io_info_ptr->TX_OUT                           = 0;
   io_info_ptr->STATISTICS.INTERRUPTS            = 0;
   io_info_ptr->STATISTICS.RX_PACKETS            = 0;
   io_info_ptr->STATISTICS.TX_PACKETS            = 0;
   io_info_ptr->STATISTICS.TX_LOST_ARBITRATIONS  = 0;
   io_info_ptr->STATISTICS.TX_ADDRESSED_AS_SLAVE = 0;
   io_info_ptr->STATISTICS.TX_NAKS               = 0;

   /* Disable and clear I2C before initializing it */
   i2c_ptr->I2CR = 0x00;
   _PSP_SYNC();
  
   /* Clear out all I2C events */
   i2c_ptr->I2SR = 0x00;
   _PSP_SYNC();

   /* Set the station address for SLAVE receive operations */      
   i2c_ptr->I2ADR = io_init_ptr->STATION_ADDRESS << 1;
   _PSP_SYNC();
   
   /* Set the frequency divider for the nearest found baud rate */
   io_info_ptr->BAUD_INDEX = _mpc83xx_i2c_find_baud_index (io_init_ptr->BAUD_RATE);
   i2c_ptr->I2FDR = _i2c_baudrate_tbl[io_info_ptr->BAUD_INDEX].IC;
   _PSP_SYNC();
   
   /* Enable I2C */
   i2c_ptr->I2CR |= MPC83xx_I2C_I2CR_IEN;
   _PSP_SYNC();

   return I2C_OK;
   
} /* Endbody */


/*FUNCTION****************************************************************
* 
* Function Name    : _mpc83xx_i2c_polled_deinit
* Returned Value   : MQX error code
* Comments         :
*    This function de-initializes an I2C device.
*
*END*********************************************************************/

_mqx_uint _mpc83xx_i2c_polled_deinit
   (
      /* [IN] the initialization information for the device being opened */
      IO_I2C_POLLED_DEVICE_STRUCT_PTR pol_io_dev_ptr,

      /* [IN] the address of the device specific information */
      VMPC83xx_I2C_INFO_STRUCT_PTR    io_info_ptr
   )
{ /* Body */
   VMPC83xx_I2C_STRUCT_PTR            i2c_ptr;

   if ((NULL == io_info_ptr) || (NULL == pol_io_dev_ptr)) 
   {
      return I2C_ERROR_INVALID_PARAMETER;
   }
      
   i2c_ptr = io_info_ptr->I2C_PTR;
   _PSP_SYNC();
   if (i2c_ptr->I2SR & MPC83xx_I2C_I2SR_IBB) 
   {
      return I2C_ERROR_DEVICE_BUSY;
   }
   
   /* Disable the I2C */
   i2c_ptr->I2CR = 0x00;
   _PSP_SYNC();

   /* Clear the I2C events */
   i2c_ptr->I2SR = 0x00; 
   _PSP_SYNC();

   /* Free info struct */
   _mem_free (pol_io_dev_ptr->DEV_INFO_PTR);
   pol_io_dev_ptr->DEV_INFO_PTR = NULL;

   return MQX_OK;

} /* Endbody */

 
/*FUNCTION****************************************************************
* 
* Function Name    : _mpc83xx_i2c_polled_ioctl
* Returned Value   : MQX error code.
* Comments         : 
*    This function performs miscellaneous services for
*    the I2C I/O device.  
*
*END*********************************************************************/

uint_32 _mpc83xx_i2c_polled_ioctl
   (
      /* [IN] the address of the device specific information */
      VMPC83xx_I2C_INFO_STRUCT_PTR io_info_ptr,

      /* [IN] The command to perform */
      uint_32                      cmd,

      /* [IN] Parameters for the command */
      uint_32_ptr                  param_ptr
   )
{ /* Body */
   VMPC83xx_I2C_STRUCT_PTR         i2c_ptr;
   uint_32                         tstatus;
   uint_32                         result = MQX_OK;
   volatile uchar                  tmp;
  
   i2c_ptr = io_info_ptr->I2C_PTR;
   
   switch (cmd) 
   {
      case IO_IOCTL_FLUSH_OUTPUT:
         do 
         {
            for (tmp = 0; tmp < 0xFF; tmp++) 
               { };
            tmp = i2c_ptr->I2SR;
            _PSP_SYNC();
         } while ((i2c_ptr->I2CR & MPC83xx_I2C_I2CR_MTX) && ((MPC83xx_I2C_I2SR_IBB == (tmp & (MPC83xx_I2C_I2SR_ICF | MPC83xx_I2C_I2SR_IBB))) || (io_info_ptr->TX_IN != io_info_ptr->TX_OUT)));
         io_info_ptr->TX_IN = io_info_ptr->TX_OUT;
         if ((io_info_ptr->OPERATION & I2C_OPERATION_STARTED) && (0 == (tmp & MPC83xx_I2C_I2SR_IBB)))
         {
             io_info_ptr->OPERATION = 0;
             io_info_ptr->RX_REQUEST = 0;
             io_info_ptr->STATE = I2C_STATE_FINISHED;
         }
         if (NULL != param_ptr) 
         {
            *param_ptr = tmp & MPC83xx_I2C_I2SR_RXAK;
         }
         break;
      case IO_IOCTL_I2C_REPEATED_START:
         result = I2C_ERROR_DEVICE_BUSY;
         if (i2c_ptr->I2CR & MPC83xx_I2C_I2CR_MSTA)
         {
            if (((I2C_STATE_TRANSMIT == io_info_ptr->STATE) && (io_info_ptr->TX_IN == io_info_ptr->TX_OUT))
               || ((I2C_STATE_RECEIVE == io_info_ptr->STATE) && (0 == io_info_ptr->RX_REQUEST))
               || (I2C_STATE_FINISHED == io_info_ptr->STATE))
            {
               io_info_ptr->STATE = I2C_STATE_REPEATED_START;
               result = MQX_OK;
            }
         }
         break;
      case IO_IOCTL_I2C_STOP:
         i2c_ptr->I2CR &= (~ (MPC83xx_I2C_I2CR_MTX | MPC83xx_I2C_I2CR_TXAK));
         _PSP_SYNC();
         i2c_ptr->I2SR &= (~ (MPC83xx_I2C_I2SR_IAL | MPC83xx_I2C_I2SR_IIF));
         _PSP_SYNC();
         io_info_ptr->RX_REQUEST = 0;
         io_info_ptr->OPERATION = 0;
         io_info_ptr->STATE = I2C_STATE_READY;
         io_info_ptr->RX_OUT = io_info_ptr->RX_IN;

         if (! (i2c_ptr->I2CR & MPC83xx_I2C_I2CR_MSTA))
         {
            /* As slave, release the bus */
            tmp = i2c_ptr->I2DR;
         }
         else
         {
            /* As master, send STOP condition */ 
            i2c_ptr->I2CR &= (~ MPC83xx_I2C_I2CR_MSTA);
         }
         _PSP_SYNC();
         for (tmp = 0; tmp < 0xFF; tmp++) 
            { };

         /* Wait for bus idle */
         tmp = i2c_ptr->I2SR;
         _PSP_SYNC();
         while (tmp & MPC83xx_I2C_I2SR_IBB)
         { 
            if ((tmp & MPC83xx_I2C_I2SR_IIF) || (io_info_ptr->INT_REQUEST))
            {
               i2c_ptr->I2SR &= (~ MPC83xx_I2C_I2SR_IIF);
               _PSP_SYNC();
               io_info_ptr->INT_REQUEST = 0;
               tmp = i2c_ptr->I2DR;
               _PSP_SYNC();
            }
            tmp = i2c_ptr->I2SR;
            _PSP_SYNC();
         }
         io_info_ptr->INT_REQUEST = 0;
         break;
      case IO_IOCTL_I2C_SET_RX_REQUEST:
         if (NULL == param_ptr) 
         {
            result = I2C_ERROR_INVALID_PARAMETER;
         } 
         else 
         {
            io_info_ptr->RX_REQUEST = *param_ptr;
         }
         break;
      case IO_IOCTL_I2C_DISABLE_DEVICE:
         i2c_ptr->I2CR &= (~ MPC83xx_I2C_I2CR_IEN);
         _PSP_SYNC();
         break;
      case IO_IOCTL_I2C_ENABLE_DEVICE:
         i2c_ptr->I2CR |= MPC83xx_I2C_I2CR_IEN;
         _PSP_SYNC();
         break;
      case IO_IOCTL_I2C_SET_MASTER_MODE:
         if (I2C_STATE_READY != io_info_ptr->STATE) 
         {
            result = I2C_ERROR_DEVICE_BUSY;
         } 
         else 
         {
            io_info_ptr->MODE = I2C_MODE_MASTER;
         }
         break;
      case IO_IOCTL_I2C_SET_SLAVE_MODE:
         if (I2C_STATE_READY != io_info_ptr->STATE) 
         {
            result = I2C_ERROR_DEVICE_BUSY;
         } 
         else 
         {
            io_info_ptr->MODE = I2C_MODE_SLAVE;
         }
         break;
      case IO_IOCTL_I2C_GET_MODE:
         if (NULL == param_ptr) 
         {
            result = I2C_ERROR_INVALID_PARAMETER;
         } 
         else 
         {
            *param_ptr = io_info_ptr->MODE;
         }
         break;
      case IO_IOCTL_I2C_SET_BAUD:
         if (NULL == param_ptr) 
         {
            result = I2C_ERROR_INVALID_PARAMETER;
         } 
         else if (i2c_ptr->I2SR & MPC83xx_I2C_I2SR_IBB) 
         {
            result = I2C_ERROR_DEVICE_BUSY;
         } 
         else 
         {
            io_info_ptr->BAUD_INDEX = _mpc83xx_i2c_find_baud_index (*param_ptr);
            i2c_ptr->I2FDR = _i2c_baudrate_tbl[io_info_ptr->BAUD_INDEX].IC;
            _PSP_SYNC();         
		 }
         break;
      case IO_IOCTL_I2C_GET_BAUD:
         if (NULL == param_ptr) 
         {
            result = I2C_ERROR_INVALID_PARAMETER;
         } 
         else 
         {
            *param_ptr = _i2c_baudrate_tbl[io_info_ptr->BAUD_INDEX].BAUD_RATE;
         }
         break;
      case IO_IOCTL_I2C_SET_DESTINATION_ADDRESS:
         if (NULL == param_ptr) 
         {
            result = I2C_ERROR_INVALID_PARAMETER;
         } 
         else 
         {
            io_info_ptr->ADDRESSEE = *param_ptr;
         }
         break;
      case IO_IOCTL_I2C_GET_DESTINATION_ADDRESS:
         if (NULL == param_ptr) 
         {
            result = I2C_ERROR_INVALID_PARAMETER;
         } 
         else 
         {
            *param_ptr = io_info_ptr->ADDRESSEE;
         }
         break;
      case IO_IOCTL_I2C_SET_STATION_ADDRESS:
         if (NULL == param_ptr) 
         {
            result = I2C_ERROR_INVALID_PARAMETER;
         } 
         else 
         {
            i2c_ptr->I2ADR = (*param_ptr) << 1;
            _PSP_SYNC();
         }
         break;
      case IO_IOCTL_I2C_GET_STATION_ADDRESS:
         if (NULL == param_ptr) 
         {
            result = I2C_ERROR_INVALID_PARAMETER;
         } 
         else 
         {
            *param_ptr = i2c_ptr->I2ADR >> 1;
            _PSP_SYNC();
         }
         break;
      case IO_IOCTL_I2C_GET_STATE:
         if (NULL == param_ptr) 
         {
            result = I2C_ERROR_INVALID_PARAMETER;
         } 
         else 
         {
            *param_ptr = io_info_ptr->STATE;
         }
         break;
      case IO_IOCTL_I2C_GET_STATISTICS:
         if (NULL == param_ptr) 
         {
            result = I2C_ERROR_INVALID_PARAMETER;
         } 
         else 
         {
            *((I2C_STATISTICS_STRUCT_PTR)param_ptr) = io_info_ptr->STATISTICS;
         }
         break;         
      case IO_IOCTL_I2C_CLEAR_STATISTICS:
         io_info_ptr->STATISTICS.INTERRUPTS = 0;
         io_info_ptr->STATISTICS.RX_PACKETS = 0;
         io_info_ptr->STATISTICS.TX_PACKETS = 0;
         io_info_ptr->STATISTICS.TX_LOST_ARBITRATIONS = 0;
         io_info_ptr->STATISTICS.TX_ADDRESSED_AS_SLAVE = 0;
         io_info_ptr->STATISTICS.TX_NAKS = 0;
         break;
      case IO_IOCTL_I2C_GET_BUS_AVAILABILITY:
         if (NULL == param_ptr) 
         {
            result = I2C_ERROR_INVALID_PARAMETER;
         } 
         else 
         {
            if (i2c_ptr->I2SR & MPC83xx_I2C_I2SR_IBB) 
            {
                *param_ptr = I2C_BUS_BUSY;
            } 
            else 
            {
                *param_ptr = I2C_BUS_IDLE;
            }
         }
         break;
      default:
         break;
   }
   return result;
} /* Endbody */


/*FUNCTION****************************************************************
* 
* Function Name    : _mpc83xx_i2c_polled_rx
* Returned Value   : number of bytes read
* Comments         : 
*   Returns the number of bytes received.
*   Reads the data into provided array when data is available.
*
*END*********************************************************************/

uint_32 _mpc83xx_i2c_polled_rx
   (
      /* [IN] the address of the device specific information */
      IO_I2C_POLLED_DEVICE_STRUCT_PTR pol_io_dev_ptr,

      /* [IN] The array to copy data into */
      char_ptr                        buffer,
      
      /* [IN] number of bytes to read */
      _mqx_int                        length
   )
{ /* Body */
   VMPC83xx_I2C_INFO_STRUCT_PTR       io_info_ptr;
   VMPC83xx_I2C_STRUCT_PTR            i2c_ptr;   
   uint_8                             tmp;
         
   io_info_ptr = pol_io_dev_ptr->DEV_INFO_PTR;
   i2c_ptr = io_info_ptr->I2C_PTR;

   /* If beginning of transmission, set state and send address (master only) */
   io_info_ptr->OPERATION |= I2C_OPERATION_READ;
   tmp = io_info_ptr->STATE;
   if ((I2C_STATE_READY == tmp) || (I2C_STATE_REPEATED_START == tmp))
   {
      io_info_ptr->STATE = I2C_STATE_RECEIVE;
      if (I2C_MODE_MASTER == io_info_ptr->MODE)
      {
         i2c_ptr->I2CR |= MPC83xx_I2C_I2CR_MTX;
         _PSP_SYNC();
         i2c_ptr->I2SR &= (~ MPC83xx_I2C_I2SR_IIF);
         _PSP_SYNC();
         if (I2C_STATE_REPEATED_START == tmp)
         {
            i2c_ptr->I2CR |= MPC83xx_I2C_I2CR_RSTA;
         }
         else
         {
            i2c_ptr->I2CR |= MPC83xx_I2C_I2CR_MSTA;
         }
         _PSP_SYNC();
         io_info_ptr->OPERATION |= I2C_OPERATION_STARTED;
         i2c_ptr->I2DR = (io_info_ptr->ADDRESSEE << 1) | I2C_OPERATION_READ;
         _PSP_SYNC();
         io_info_ptr->STATISTICS.TX_PACKETS++;
         while (0 == (i2c_ptr->I2SR & MPC83xx_I2C_I2SR_IIF)) 
            {_PSP_SYNC();};   /* wait for address transferred */
      }
   }

   return _mpc83xx_i2c_polled_rx_tx (i2c_ptr, io_info_ptr, buffer, length);
} /* Endbody */


/*FUNCTION****************************************************************
* 
* Function Name    : _mpc83xx_i2c_polled_tx
* Returned Value   : number of bytes transmitted
* Comments         : 
*   Writes the provided data buffer and loops until transmission complete.
*
*END*********************************************************************/

uint_32 _mpc83xx_i2c_polled_tx
   (
      /* [IN] the address of the device specific information */
      IO_I2C_POLLED_DEVICE_STRUCT_PTR pol_io_dev_ptr,

      /* [IN] The array characters are to be read from */
      char_ptr                        buffer,
      
      /* [IN] number of bytes to output */
      _mqx_int                        length
   )
{ /* Body */
   VMPC83xx_I2C_INFO_STRUCT_PTR       io_info_ptr;
   VMPC83xx_I2C_STRUCT_PTR            i2c_ptr;   
   uint_8                             tmp;
   
   io_info_ptr  = pol_io_dev_ptr->DEV_INFO_PTR;
   i2c_ptr = io_info_ptr->I2C_PTR;

   /* If beginning of transmission, set state and send address (master only) */
   io_info_ptr->OPERATION &= (~ I2C_OPERATION_READ);
   tmp = io_info_ptr->STATE;
   if ((I2C_STATE_READY == tmp) || (I2C_STATE_REPEATED_START == tmp))
   {
      io_info_ptr->STATE = I2C_STATE_TRANSMIT;
      if (I2C_MODE_MASTER == io_info_ptr->MODE)
      {
         i2c_ptr->I2CR |= MPC83xx_I2C_I2CR_MTX;
         _PSP_SYNC();
         i2c_ptr->I2SR &= (~ MPC83xx_I2C_I2SR_IIF);
         _PSP_SYNC();
         if (I2C_STATE_REPEATED_START == tmp)
         {
            i2c_ptr->I2CR |= MPC83xx_I2C_I2CR_RSTA;
         }
         else
         {
            i2c_ptr->I2CR |= MPC83xx_I2C_I2CR_MSTA;
         }
         _PSP_SYNC();
         io_info_ptr->OPERATION |= I2C_OPERATION_STARTED;
         i2c_ptr->I2DR = (io_info_ptr->ADDRESSEE << 1) | I2C_OPERATION_WRITE;
         _PSP_SYNC();
         io_info_ptr->STATISTICS.TX_PACKETS++;
         while (0 == (i2c_ptr->I2SR & MPC83xx_I2C_I2SR_IIF))
            {_PSP_SYNC();};   /* wait for address transferred */
      }
   }

   return _mpc83xx_i2c_polled_rx_tx (i2c_ptr, io_info_ptr, buffer, length);
} /* Endbody */


/*FUNCTION****************************************************************
* 
* Function Name    : _mpc83xx_i2c_polled_rx_tx
* Returned Value   : number of bytes processed
* Comments         : 
*   Actual data transfer on I2C bus.
*
*END*********************************************************************/

static uint_32 _mpc83xx_i2c_polled_rx_tx
   (
      /* [IN] I2C register structure */
      VMPC83xx_I2C_STRUCT_PTR      i2c_ptr,

      /* [IN] I2C state structure */
      VMPC83xx_I2C_INFO_STRUCT_PTR io_info_ptr,
      
      /* [IN] The buffer for IO operation */
      char_ptr                     buffer,
      
      /* [IN] Number of bytes in buffer */
      uint_32                      length
   )
{ /* Body */
   uint_32                         i;
   uint_8                          i2csr;
   volatile uint_8                 tmp;

   for (i = 0; i <= length; i++)
   {
      do 
      {
         i2csr = i2c_ptr->I2SR;
         _PSP_SYNC();
         if (0 == (i2c_ptr->I2CR & MPC83xx_I2C_I2CR_MSTA))
         {
            if (i == length)
            {
               return i;
            }
            if ((io_info_ptr->OPERATION & I2C_OPERATION_STARTED) && (0 == (i2csr & MPC83xx_I2C_I2SR_IBB)))
            {
               io_info_ptr->OPERATION = 0;
               io_info_ptr->RX_REQUEST = 0;
               io_info_ptr->STATE = I2C_STATE_FINISHED;
               return i;
            }
         }
      } while (0 == (i2csr & MPC83xx_I2C_I2SR_IIF));

      io_info_ptr->OPERATION |= I2C_OPERATION_STARTED;
   
      /* Master */
      if (i2c_ptr->I2CR & MPC83xx_I2C_I2CR_MSTA)
      {
         /* Transmit */
         if (i2c_ptr->I2CR & MPC83xx_I2C_I2CR_MTX)
         {
            /* Not ack */
            if (i2csr & MPC83xx_I2C_I2SR_RXAK)
            {
               i2c_ptr->I2SR &= (~ MPC83xx_I2C_I2SR_IIF);
               _PSP_SYNC();
               io_info_ptr->STATE = I2C_STATE_FINISHED;
               io_info_ptr->STATISTICS.TX_NAKS++;
               length = i;
            }
            /* Ack */
            else
            {
               /* Transmit requested */
               if (I2C_STATE_TRANSMIT == io_info_ptr->STATE)
               {
                  if (0 == (io_info_ptr->OPERATION & I2C_OPERATION_READ))
                  {
                     /* Anything to transmit? */
                     if (i < length)
                     {
                        i2c_ptr->I2SR &= (~ MPC83xx_I2C_I2SR_IIF);
                        _PSP_SYNC();
                        i2c_ptr->I2DR = *buffer++;   /* transmit data */
                        _PSP_SYNC();
                        io_info_ptr->STATISTICS.TX_PACKETS++;
                     }
                  }
                  else
                  {
                     length = i;
                  }
               }
               /* Receive requested */
               else if (I2C_STATE_RECEIVE == io_info_ptr->STATE)
               {
                  if (0 != (io_info_ptr->OPERATION & I2C_OPERATION_READ))
                  {
                     i2c_ptr->I2CR &= (~ MPC83xx_I2C_I2CR_MTX);
                     _PSP_SYNC();
                     if (1 == io_info_ptr->RX_REQUEST)
                     {
                        i2c_ptr->I2CR |= MPC83xx_I2C_I2CR_TXAK;
                     }
                     else
                     {
                        i2c_ptr->I2CR &= (~ MPC83xx_I2C_I2CR_TXAK);
                     }
                     _PSP_SYNC();
                     i2c_ptr->I2SR &= (~ MPC83xx_I2C_I2SR_IIF);
                     _PSP_SYNC();
                     if (0 == io_info_ptr->RX_REQUEST)
                     {
                        io_info_ptr->STATE = I2C_STATE_FINISHED;
                        length = i;
                     }
                     else
                     {
                        tmp = i2c_ptr->I2DR;   // dummy read to clock in 1st byte
                        _PSP_SYNC();
                        i--;
                     }
                  }
                  else
                  {
                     length = i;
                  }
               }
               /* Others are unwanted/not handled states */
               else
               {
                  length = i;
               }
            }
         }
         /* Receive */
         else
         {
            /* Receive requested */
            if (I2C_STATE_RECEIVE == io_info_ptr->STATE)
            {
               if ((0 != (io_info_ptr->OPERATION & I2C_OPERATION_READ)) && (0 != io_info_ptr->RX_REQUEST))
               {
                  if (i < length)
                  {
                     i2c_ptr->I2SR &= (~ MPC83xx_I2C_I2SR_IIF);
                     _PSP_SYNC();
                     io_info_ptr->RX_REQUEST--;
                     if (1 == io_info_ptr->RX_REQUEST)
                     {
                        i2c_ptr->I2CR |= MPC83xx_I2C_I2CR_TXAK;
                     }
                     else
                     {
                        i2c_ptr->I2CR &= (~ MPC83xx_I2C_I2CR_TXAK);
                     }
                     _PSP_SYNC();
                     if (0 == io_info_ptr->RX_REQUEST)
                     {
                        i2c_ptr->I2CR |= MPC83xx_I2C_I2CR_MTX;   /* no more reading */
                        _PSP_SYNC();
                     }
                     *buffer++ = i2c_ptr->I2DR;   /* receive data */
                     _PSP_SYNC();
                     if (0 == io_info_ptr->RX_REQUEST)
                     {
                        i2c_ptr->I2CR &= (~ MPC83xx_I2C_I2CR_MTX);
                        _PSP_SYNC();
                        io_info_ptr->STATE = I2C_STATE_FINISHED;
                        length = i + 1;
                        i = length;
                     }
                     io_info_ptr->STATISTICS.RX_PACKETS++;
                  }
               }
               else
               {
                  length = i;
               }
            }
            /* Others are unwanted/not handled states */
            else 
            {
               length = i;
            }
         }
      }
      /* Slave */
      else
      {
         /* Master arbitration lost */
         if (i2csr & MPC83xx_I2C_I2SR_IAL)
         {
            i2c_ptr->I2SR &= (~ MPC83xx_I2C_I2SR_IAL);
            _PSP_SYNC();
            io_info_ptr->STATE = I2C_STATE_LOST_ARBITRATION;
            io_info_ptr->STATISTICS.TX_LOST_ARBITRATIONS++;
            length = i;
         }
         /* Addressed as slave */
         if (i2csr & MPC83xx_I2C_I2SR_IAAS)
         {
            if (I2C_MODE_MASTER == io_info_ptr->MODE)
            {
               io_info_ptr->STATISTICS.TX_ADDRESSED_AS_SLAVE++;
            }
            /* Transmit requested */
            if (i2csr & MPC83xx_I2C_I2SR_SRW)
            {  
               io_info_ptr->STATE = I2C_STATE_ADDRESSED_AS_SLAVE_TX;
               if (0 == (io_info_ptr->OPERATION & I2C_OPERATION_READ))
               {
                  if (i < length)
                  {
                     i2c_ptr->I2CR |= MPC83xx_I2C_I2CR_MTX;
                     _PSP_SYNC();
                     i2c_ptr->I2SR &= (~ MPC83xx_I2C_I2SR_IIF);
                     _PSP_SYNC();
                     i2c_ptr->I2DR = *buffer++;   /* transmit data */
                     _PSP_SYNC();
                     io_info_ptr->STATISTICS.TX_PACKETS++;
                  }
               }
               else
               {
                  length = i;
               }
            }
            /* Receive requested */
            else
            {
               io_info_ptr->STATE = I2C_STATE_ADDRESSED_AS_SLAVE_RX;
               if ((0 != (io_info_ptr->OPERATION & I2C_OPERATION_READ)) && (i == 0) && (0 != io_info_ptr->RX_REQUEST))
               {
                  i2c_ptr->I2CR &= (~ MPC83xx_I2C_I2CR_MTX);
                  _PSP_SYNC();
                  i2c_ptr->I2SR &= (~ MPC83xx_I2C_I2SR_IIF);
                  _PSP_SYNC();
                  if (1 == io_info_ptr->RX_REQUEST)
                  {
                     i2c_ptr->I2CR |= MPC83xx_I2C_I2CR_TXAK;
                  }
                  else
                  {
                     i2c_ptr->I2CR &= (~ MPC83xx_I2C_I2CR_TXAK);
                  }
                  _PSP_SYNC();
                  tmp = i2c_ptr->I2DR;   /* dummy read to release bus */
                  _PSP_SYNC();
                  i--;
               }
               else
               {
                  length = i;
               }
            }
         }
         /* Normal slave operation */
         else
         {
            /* No master arbitration lost */
            if (! (i2csr & MPC83xx_I2C_I2SR_IAL))
            {
               /* Transmit */
               if (i2c_ptr->I2CR & MPC83xx_I2C_I2CR_MTX)
               {
                  /* Not ack */
                  if (i2csr & MPC83xx_I2C_I2SR_RXAK)
                  {
                     i2c_ptr->I2SR &= (~ MPC83xx_I2C_I2SR_IIF);
                     _PSP_SYNC();
                     io_info_ptr->STATE = I2C_STATE_FINISHED;
                     io_info_ptr->STATISTICS.TX_NAKS++;
                     length = i;
                  }
                  /* Ack */
                  else
                  {
                     /* Transmit requested */
                     if (((I2C_STATE_TRANSMIT == io_info_ptr->STATE) || (I2C_STATE_ADDRESSED_AS_SLAVE_TX == io_info_ptr->STATE))
                        && (0 == (io_info_ptr->OPERATION & I2C_OPERATION_READ)))
                     {
                        if (i < length)
                        {
                           i2c_ptr->I2SR &= (~ MPC83xx_I2C_I2SR_IIF);
                           _PSP_SYNC();
                           i2c_ptr->I2DR = *buffer++;   /* transmit data */
                           _PSP_SYNC();
                           io_info_ptr->STATISTICS.TX_PACKETS++;
                        }
                     }
                     else
                     {
                        length = i;
                     }
                  }
               }
               /* Receive */
               else
               {
                  /* Receive requested */
                  if (((I2C_STATE_RECEIVE == io_info_ptr->STATE) || (I2C_STATE_ADDRESSED_AS_SLAVE_RX == io_info_ptr->STATE))
                     && (0 != (io_info_ptr->OPERATION & I2C_OPERATION_READ)) && (0 != io_info_ptr->RX_REQUEST))
                  {
                     if (i < length)
                     {
                        i2c_ptr->I2SR &= (~ MPC83xx_I2C_I2SR_IIF);
                        _PSP_SYNC();
                        io_info_ptr->RX_REQUEST--;
                        if (1 == io_info_ptr->RX_REQUEST)
                        {
                           i2c_ptr->I2CR |= MPC83xx_I2C_I2CR_TXAK;
                        }
                        else
                        {
                           i2c_ptr->I2CR &= (~ MPC83xx_I2C_I2CR_TXAK);
                        }
                        _PSP_SYNC();
                        if (0 == io_info_ptr->RX_REQUEST)
                        {
                           io_info_ptr->STATE = I2C_STATE_FINISHED;
                        }
                        *buffer++ = i2c_ptr->I2DR;   /* receive data */
                        _PSP_SYNC();
                        io_info_ptr->STATISTICS.RX_PACKETS++;
                     }
                  }
                  else
                  {
                     length = i;
                  }
               }
            }
            else
            {
               length = i;
            }
         }
      }
   }
   return length;
} /* Endbody */

/* EOF */
